/**
 * Implementacion de la clase EdgeValidator.
 */

#include "edgeValidator.hpp"
#include "../config/config.hpp"
#include "../main/edge.hpp"
#include <stdlib.h>
#include <sstream>

using namespace std;

void EdgeValidator::create(std::string &fileToParse, Logger *log, Graph *graph)
{
	this->setParser(fileToParse,log);
	this->logger = log;
	int number = atoi(Config::get("number_of_fields_expected_edges").c_str());
	if(number > 0)
	{
		this->numberOfFieldsExpected = number;
		this->edge.setSuccessStatus(9);
	}
	else
	{
		std::string detail = "Cantidad de campos esperados para la validacion de las aristas no tiene el formato esperado";
		this->logger->reportErrorMessage("EdgeValidator","create",detail);
		this->edge.setFailStatus(9);
	}
	this->graph = graph;
	
}

EdgeValidator::~EdgeValidator()
{
	this->logger = 0;
	this->numberOfFieldsExpected = 0;
}

void EdgeValidator::validateNumberOfFields(int lineCount, int fieldAmount)
{
	if(fieldAmount != this->numberOfFieldsExpected)
	{
		this->edge.setFailStatus(6);
		std::string detail = "Cantidad de campos distinta a la esperada";
		this->logger->reportErrorMessage("EdgeValidator","validateNumberOfFields", detail, lineCount);
	}
	else
		this->edge.setSuccessStatus(6);

}

void EdgeValidator::validateField(int lineCount, int fieldNumber, std::string field)
{
	std::string detail;
	if((fieldNumber <= 3)||(fieldNumber == 7))
	{
		if((std::string::npos == field.find_first_of(','))&&(std::string::npos == field.find_first_of('.'))
			&&(field[0] != '0')&&(atoi(field.c_str()) == 0))
		{
			if(fieldNumber == 1)
				this->edge.setEdgeId(field);
			if(fieldNumber == 2)
				this->edge.setNodeOriginId(field);
			if(fieldNumber == 3)
				this->edge.setNodeDestinyId(field);
			if(fieldNumber == 7)
				this->edge.setRoadName(field);
		}
		else
		{
			detail = "Esperado un literal, se encontro un real";
			this->logger->reportErrorMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			this->edge.setFailStatus(fieldNumber);
		}
	}
	else if((fieldNumber == 4)||(fieldNumber == 5))
	{
		double numero = atof(field.c_str());
		if(numero > 0)
		{
			if(fieldNumber == 4)
				this->edge.setLongitude((float)numero);
			else
				this->edge.setVelocity((float)numero);
		}
		else if (numero == 0)
		{
			detail = "Esperado un real, se encontro un literal, o el real vale cero";
			this->logger->reportErrorMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			this->edge.setFailStatus(fieldNumber);
		}
		else if(numero < 0)
		{
			detail = "El valor es negativo";
			this->logger->reportErrorMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			this->edge.setFailStatus(fieldNumber);
		}
	}
	else if(fieldNumber == 6)
	{
		if(strcmp("calle",field.c_str()) == 0)
			this->edge.setRoadType(CALLE);
		else if (strcmp("avenida",field.c_str()) == 0)
			this->edge.setRoadType(AVENIDA);
		else if (strcmp("autopista",field.c_str()) == 0)
			this->edge.setRoadType(AUTOPISTA);
		else
		{
			detail = "Se encontro un tipo de camino no esperado";
			this->logger->reportErrorMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			this->edge.setFailStatus(fieldNumber);
		}		
	}
	else if(fieldNumber == 8)
	{
		int position = field.find('-');
		if(position == std::string::npos)
		{
			detail = "Formato de altura comprendida distinta a la esperada";
			this->logger->reportErrorMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			this->edge.setFailStatus(fieldNumber);
		}
		else
		{
			int altIni = atoi(field.substr(0,position).c_str());
			if(altIni == 0)
			{
				detail = "La altura inicial es cero, o es un literal";
				this->logger->reportWarningMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			}

			int altFin = atoi(field.substr(position+1).c_str());
			if(altFin == 0)
			{
				detail = "La altura final es cero, o es un literal";
				this->logger->reportWarningMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
			}

			if((altIni >= 0)&&(altFin >= 0))
			{
				this->edge.setInitialHeight(altIni);
				this->edge.setFinalHeight(altFin);
			}
			else
			{
				detail = "Formato de altura comprendida distinta a la esperada";
				this->logger->reportWarningMessage("EdgeValidator","validateField", detail,lineCount,fieldNumber);
				this->edge.setFailStatus(fieldNumber);
			}
		}
	}
}

void EdgeValidator::validateElementAdding(int lineCount)
{
	bool success = true;
	std::string detail;

	if(this->edge.getEdgeIdStatus() == 0)
		success = false;
	if(this->edge.getNodeOriginIdStatus() == 0)
		success = false;
	if(this->edge.getNodeDestinyIdStatus() == 0)
		success = false;
	if(this->edge.getLongitudeStatus() == 0)
		success = false;
	if(this->edge.getVelocityStatus() == 0)
		success = false;
	if(this->edge.getNumberOfFieldsStatus() == 0)
		success = false;
	if(this->edge.getRoadTypeStatus() == 0)
		success = false;
	if(this->edge.getRoadNameStatus() == 0)
		success = false;
	if(this->edge.getInitialHeightStatus() == 0)
		success = false;
	if(this->edge.getFinalHeightStatus() == 0)
		success = false;


	if(success)
	{
		// TODO Nico: optimizar el siguiente codigo en lo posible
		Node *nodeOrigin = this->graph->getNode(this->edge.getNodeOriginId());
		Node *nodeDestiny = this->graph->getNode(this->edge.getNodeDestinyId());
		

		//La calle va de la altura menor a la altura mayor -1
		if(this->edge.getInitialHeight() > this->edge.getFinalHeight())
		{
			this->edge.setInitialHeight(this->edge.getInitialHeight()-1);
		}
		else
		{
			this->edge.setFinalHeight(this->edge.getFinalHeight()-1);
		}


		if((nodeOrigin != NULL)&&(nodeDestiny != NULL))
		{

			Edge *tempEgde = this->graph->getEdge(this->edge.getEdgeId(),this->edge.getNodeOriginId());


			if(tempEgde == NULL)
			{

				if(!graph->checkSolapation(this->edge.getRoadName(),this->edge.getInitialHeight(),this->edge.getFinalHeight(),this->edge.getNodeOriginId(),this->edge.getNodeDestinyId()))
				{
					Weight w(this->edge.getVelocity(),this->edge.getLongitude());				

					Edge *newEdge = new Edge(this->edge.getEdgeId(),this->edge.getNodeOriginId(),
						this->edge.getNodeDestinyId(), w,this->edge.getInitialHeight(),this->edge.getFinalHeight(),this->edge.getRoadName(),this->edge.getRoadType());

					

					this->graph->addEdge(newEdge);

					detail = "Arista agregada, ";
					detail += this->edge.getEdgeId();
					this->logger->reportSuccessMessage("EdgeValidator","validateElementAdding",detail);
				}
				else
				{
					detail = "Arista se solapa en alturas, id: "; detail += this->edge.getEdgeId();
					this->logger->reportErrorMessage("EdgeValidator","validateElementAdding",detail,lineCount);
				}

			}
			else
			{
				detail = "Arista con Id existente descartada, id: "; detail += this->edge.getEdgeId();
				this->logger->reportErrorMessage("EdgeValidator","validateElementAdding",detail,lineCount);

			}
		}
		else if(nodeOrigin == NULL)
		{
			detail = "Id de nodo origen de arista inexistente, arista descartada, id: "; detail += this->edge.getEdgeId();
			this->logger->reportErrorMessage("EdgeValidator","validateElementAdding",detail,lineCount);
		}
		else if(nodeDestiny == NULL)
		{
			detail = "Id de nodo destino de arista inexistente, arista descartada, id: "; detail += this->edge.getEdgeId();
			this->logger->reportErrorMessage("EdgeValidator","validateElementAdding",detail,lineCount);
		}

	}
	else
	{
		std::string detail = "Arista descartada";
		this->logger->reportErrorMessage("EdgeValidator","validateElementAdding",detail);
		this->edge.clear();

	}
}
